package session

import (
	"context"
	"fmt"
	"net"
	"src/iinf"
	"src/log"
)

//ClientSession ClientSession
type ClientSession struct {
	iinf.SessionBase
	id         int
	status     string
	connClient net.Conn
	connDev    net.Conn
	ctx        context.Context
	cancelFunc context.CancelFunc
}

func (cs *ClientSession) setQuit() {
	cs.status = "quit"
}
func (cs *ClientSession) goClient2Dev() {
	defer cs.setQuit()
	var data [1024]byte
	for {
		select {
		case <-cs.ctx.Done():
			log.Println("goClient2Dev ctx done exit")
			return
		default:
			n, err := cs.connClient.Read(data[0:])
			if err != nil {
				log.Println(err)
				return
			}
			n, err = cs.connDev.Write(data[:n])
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (cs *ClientSession) goDev2Client() {
	defer cs.setQuit()
	var data [1024]byte
	for {
		select {
		case <-cs.ctx.Done():
			log.Println("goDev2Client ctx done exit")
			return
		default:
			n, err := cs.connDev.Read(data[0:])
			if err != nil {
				log.Println(err)
				return
			}
			n, err = cs.connClient.Write(data[:n])
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

//IsQuit IsQuit
func (cs *ClientSession) IsQuit() bool {
	if cs.status == "quit" {
		return true
	}

	return false
}

// Close Close
func (cs *ClientSession) Close() {
	cs.setQuit()
	cs.cancelFunc()

	if cs.connClient != nil {
		cs.connClient.Close()
		cs.connClient = nil
	}
	if cs.connDev != nil {
		cs.connDev.Close()
		cs.connDev = nil
	}

	log.Println("ClientSession id=", cs.id, "close over")
}

//Show Show
func (cs *ClientSession) Show() {
	log.Println(cs.GetInfo())
}

//GetInfo GetInfo
func (cs *ClientSession) GetInfo() string {
	//check
	if cs.connClient == nil ||
		cs.connDev == nil {
		return ""
	}
	str := fmt.Sprintf("******************id=%d status=%s client=%s dev=%s \r\n",
		cs.id, cs.status, cs.connClient.RemoteAddr().String(),
		cs.connDev.RemoteAddr().String())

	return str
}

//Init Init
func (cs *ClientSession) Init(args ...interface{}) bool {
	log.Println("init  client")
	conn, ok := args[0].(net.Conn)
	if !ok {
		cs.setQuit()
		return false
	}
	cs.connDev = conn
	//开启两协程，专门做中转
	go cs.goClient2Dev()
	go cs.goDev2Client()
	cs.status = "running"

	return true
}

//GetID GetID
func (cs *ClientSession) GetID() int {
	return cs.id
}

// GetNewClientSession GetNewClientSession
func GetNewClientSession(id int, client net.Conn) *ClientSession {
	ses := &ClientSession{
		id:         id,
		connClient: client,
		status:     "idle",
	}
	ses.ctx, ses.cancelFunc = context.WithCancel(context.Background())

	return ses
}
